Jelajahi kekuatan modul ast Python untuk manipulasi abstract syntax tree. Pelajari cara menganalisis, memodifikasi, dan menghasilkan kode Python secara terprogram.
Modul Ast Python: Demistifikasi Manipulasi Abstract Syntax Tree
Modul ast
Python menyediakan cara yang ampuh untuk berinteraksi dengan abstract syntax tree (AST) dari kode Python. AST adalah representasi pohon dari struktur sintaksis kode sumber, yang memungkinkan untuk menganalisis, memodifikasi, dan bahkan menghasilkan kode Python secara terprogram. Hal ini membuka pintu ke berbagai aplikasi, termasuk alat analisis kode, refactoring otomatis, analisis statis, dan bahkan ekstensi bahasa kustom. Artikel ini akan memandu Anda melalui dasar-dasar modul ast
, memberikan contoh praktis dan wawasan tentang kemampuannya.
Apa itu Abstract Syntax Tree (AST)?
Sebelum mendalami modul ast
, mari kita pahami apa itu Abstract Syntax Tree. Ketika interpreter Python mengeksekusi kode Anda, langkah pertama adalah mengurai kode menjadi AST. Struktur pohon ini merepresentasikan elemen sintaksis kode, seperti fungsi, kelas, perulangan, ekspresi, dan operator, beserta hubungannya. AST membuang detail yang tidak relevan seperti spasi putih dan komentar, berfokus pada informasi struktural yang esensial. Dengan merepresentasikan kode dengan cara ini, program dapat menganalisis dan memanipulasi kode itu sendiri, yang sangat berguna dalam banyak situasi.
Memulai dengan Modul ast
Modul ast
adalah bagian dari pustaka standar Python, jadi Anda tidak perlu menginstal paket tambahan apa pun. Cukup impor untuk mulai menggunakannya:
import ast
Fungsi inti dari modul ast
adalah ast.parse()
, yang menerima string kode Python sebagai masukan dan mengembalikan objek AST.
code = """
def add(x, y):
return x + y
"""
ast_tree = ast.parse(code)
print(ast_tree)
Ini akan menghasilkan sesuatu seperti: <_ast.Module object at 0x...>
. Meskipun keluaran ini tidak terlalu informatif, ini menunjukkan bahwa kode berhasil diurai menjadi AST. Objek ast_tree
sekarang berisi seluruh struktur dari kode yang diurai.
Menjelajahi AST
Untuk memahami struktur AST, kita dapat menggunakan fungsi ast.dump()
. Fungsi ini secara rekursif melintasi pohon dan mencetak representasi detail dari setiap node.
code = """
def add(x, y):
return x + y
"""
ast_tree = ast.parse(code)
print(ast.dump(ast_tree, indent=4))
Keluaran akan menjadi:
Module(
body=[
FunctionDef(
name='add',
args=arguments(
posonlyargs=[],
args=[
arg(arg='x', annotation=None, type_comment=None),
arg(arg='y', annotation=None, type_comment=None)
],
kwonlyargs=[],
kw_defaults=[],
defaults=[]
),
body=[
Return(
value=BinOp(
left=Name(id='x', ctx=Load()),
op=Add(),
right=Name(id='y', ctx=Load())
)
)
],
decorator_list=[],
returns=None,
type_comment=None
)
],
type_ignores=[]
)
Keluaran ini menunjukkan struktur hierarkis dari kode. Mari kita uraikan:
Module
: Node akar yang merepresentasikan seluruh modul.body
: Daftar pernyataan di dalam modul.FunctionDef
: Merepresentasikan definisi fungsi. Atributnya meliputi:name
: Nama fungsi ('add').args
: Argumen dari fungsi.arguments
: Berisi informasi tentang argumen fungsi.arg
: Merepresentasikan satu argumen (mis., 'x', 'y').body
: Isi dari fungsi (daftar pernyataan).Return
: Merepresentasikan pernyataan return.value
: Nilai yang dikembalikan.BinOp
: Merepresentasikan operasi biner (mis., x + y).left
: Operand kiri (mis., 'x').op
: Operator (mis., 'Add').right
: Operand kanan (mis., 'y').
Melintasi AST
Modul ast
menyediakan kelas ast.NodeVisitor
untuk melintasi AST. Dengan membuat subkelas dari ast.NodeVisitor
dan mengganti metodenya, Anda dapat memproses tipe node tertentu saat ditemui selama penelusuran. Ini berguna untuk menganalisis struktur kode, mengidentifikasi pola spesifik, atau mengekstrak informasi.
import ast
class FunctionNameExtractor(ast.NodeVisitor):
def __init__(self):
self.function_names = []
def visit_FunctionDef(self, node):
self.function_names.append(node.name)
code = """
def add(x, y):
return x + y
def subtract(x, y):
return x - y
"""
ast_tree = ast.parse(code)
extractor = FunctionNameExtractor()
extractor.visit(ast_tree)
print(extractor.function_names) # Keluaran: ['add', 'subtract']
Dalam contoh ini, FunctionNameExtractor
mewarisi dari ast.NodeVisitor
dan mengganti metode visit_FunctionDef
. Metode ini dipanggil untuk setiap node definisi fungsi di AST. Metode ini menambahkan nama fungsi ke daftar function_names
. Metode visit()
memulai penelusuran AST.
Contoh: Menemukan semua penetapan variabel
import ast
class VariableAssignmentFinder(ast.NodeVisitor):
def __init__(self):
self.assignments = []
def visit_Assign(self, node):
for target in node.targets:
if isinstance(target, ast.Name):
self.assignments.append(target.id)
code = """
x = 10
y = x + 5
message = "hello"
"""
ast_tree = ast.parse(code)
finder = VariableAssignmentFinder()
finder.visit(ast_tree)
print(finder.assignments) # Keluaran: ['x', 'y', 'message']
Contoh ini menemukan semua penetapan variabel dalam kode. Metode visit_Assign
dipanggil untuk setiap pernyataan penetapan. Ini mengulangi target dari penetapan dan, jika target adalah nama sederhana (ast.Name
), ia menambahkan nama tersebut ke daftar assignments
.
Memodifikasi AST
Modul ast
juga memungkinkan Anda untuk memodifikasi AST. Anda dapat mengubah node yang ada, menambahkan node baru, atau menghapus node sama sekali. Untuk memodifikasi AST, Anda menggunakan kelas ast.NodeTransformer
. Mirip dengan ast.NodeVisitor
, Anda membuat subkelas dari ast.NodeTransformer
dan mengganti metodenya untuk memodifikasi tipe node tertentu. Perbedaan utamanya adalah metode ast.NodeTransformer
harus mengembalikan node yang dimodifikasi (atau node baru untuk menggantikannya). Jika sebuah metode mengembalikan None
, node tersebut akan dihapus dari AST.
Setelah memodifikasi AST, Anda perlu mengkompilasinya kembali menjadi kode Python yang dapat dieksekusi menggunakan fungsi compile()
.
import ast
class AddOneTransformer(ast.NodeTransformer):
def visit_Num(self, node):
return ast.Num(n=node.n + 1)
code = """
x = 10
y = 20
"""
ast_tree = ast.parse(code)
transformer = AddOneTransformer()
new_ast_tree = transformer.visit(ast_tree)
new_code = compile(new_ast_tree, '', 'exec')
# Eksekusi kode yang dimodifikasi
exec(new_code)
print(x) # Keluaran: 11
print(y) # Keluaran: 21
Dalam contoh ini, AddOneTransformer
mewarisi dari ast.NodeTransformer
dan mengganti metode visit_Num
. Metode ini dipanggil untuk setiap node literal numerik (ast.Num
). Metode ini membuat node ast.Num
baru dengan nilai yang ditambah 1. Metode visit()
mengembalikan AST yang dimodifikasi.
Fungsi compile()
mengambil AST yang dimodifikasi, nama file (<string>
dalam kasus ini, menunjukkan bahwa kode berasal dari string), dan mode eksekusi ('exec'
untuk mengeksekusi blok kode). Ini mengembalikan objek kode yang dapat dieksekusi menggunakan fungsi exec()
.
Contoh: Mengganti nama variabel
import ast
class VariableNameReplacer(ast.NodeTransformer):
def __init__(self, old_name, new_name):
self.old_name = old_name
self.new_name = new_name
def visit_Name(self, node):
if node.id == self.old_name:
return ast.Name(id=self.new_name, ctx=node.ctx)
return node
code = """
def multiply_by_two(number):
return number * 2
result = multiply_by_two(5)
print(result)
"""
ast_tree = ast.parse(code)
replacer = VariableNameReplacer('number', 'num')
new_ast_tree = replacer.visit(ast_tree)
new_code = compile(new_ast_tree, '', 'exec')
# Eksekusi kode yang dimodifikasi
exec(new_code)
Contoh ini menggantikan semua kemunculan nama variabel 'number'
dengan 'num'
. VariableNameReplacer
mengambil nama lama dan baru sebagai argumen. Metode visit_Name
dipanggil untuk setiap node nama. Jika pengidentifikasi node cocok dengan nama lama, ia membuat node ast.Name
baru dengan nama baru dan konteks yang sama (node.ctx
). Konteks menunjukkan bagaimana nama tersebut digunakan (mis., memuat, menyimpan).
Menghasilkan Kode dari AST
Meskipun compile()
memungkinkan Anda untuk mengeksekusi kode dari AST, ia tidak menyediakan cara untuk mendapatkan kode sebagai string. Untuk menghasilkan kode Python dari AST, Anda dapat menggunakan pustaka astunparse
. Pustaka ini bukan bagian dari pustaka standar, jadi Anda perlu menginstalnya terlebih dahulu:
pip install astunparse
Kemudian, Anda dapat menggunakan fungsi astunparse.unparse()
untuk menghasilkan kode dari AST.
import ast
import astunparse
code = """
def add(x, y):
return x + y
"""
ast_tree = ast.parse(code)
generated_code = astunparse.unparse(ast_tree)
print(generated_code)
Keluaran akan menjadi:
def add(x, y):
return (x + y)
Catatan: Tanda kurung di sekitar (x + y)
ditambahkan oleh astunparse
untuk memastikan presedensi operator yang benar. Tanda kurung ini mungkin tidak terlalu diperlukan, tetapi menjamin kebenaran kode.
Contoh: Menghasilkan kelas sederhana
import ast
import astunparse
class_name = 'MyClass'
method_name = 'my_method'
# Buat node definisi kelas
class_def = ast.ClassDef(
name=class_name,
bases=[],
keywords=[],
body=[
ast.FunctionDef(
name=method_name,
args=ast.arguments(
posonlyargs=[],
args=[],
kwonlyargs=[],
kw_defaults=[],
defaults=[]
),
body=[
ast.Pass()
],
decorator_list=[],
returns=None,
type_comment=None
)
],
decorator_list=[]
)
# Buat node modul yang berisi definisi kelas
module = ast.Module(body=[class_def], type_ignores=[])
# Hasilkan kode
code = astunparse.unparse(module)
print(code)
Contoh ini menghasilkan kode Python berikut:
class MyClass:
def my_method():
pass
Ini menunjukkan cara membangun AST dari awal dan kemudian menghasilkan kode darinya. Pendekatan ini sangat kuat untuk alat pembuatan kode dan metaprogramming.
Aplikasi Praktis dari Modul ast
Modul ast
memiliki banyak aplikasi praktis, termasuk:
- Analisis Kode: Menganalisis kode untuk pelanggaran gaya, kerentanan keamanan, atau hambatan kinerja. Misalnya, Anda bisa menulis alat untuk memberlakukan standar pengkodean di seluruh proyek besar.
- Refactoring Otomatis: Mengotomatiskan tugas seperti mengganti nama variabel, mengekstrak metode, atau mengonversi kode untuk menggunakan fitur bahasa yang lebih baru. Alat seperti `rope` memanfaatkan AST untuk kemampuan refactoring yang kuat.
- Analisis Statis: Mengidentifikasi potensi kesalahan atau bug dalam kode tanpa menjalankannya. Alat seperti `pylint` dan `flake8` menggunakan analisis AST untuk mendeteksi masalah.
- Pembuatan Kode: Menghasilkan kode secara otomatis berdasarkan template atau spesifikasi. Ini berguna untuk membuat kode yang berulang atau menghasilkan kode untuk platform yang berbeda.
- Ekstensi Bahasa: Membuat ekstensi bahasa kustom atau domain-specific languages (DSL) dengan mengubah kode Python menjadi representasi yang berbeda.
- Audit Keamanan: Menganalisis kode untuk konstruksi atau kerentanan yang berpotensi berbahaya. Ini dapat digunakan untuk mengidentifikasi praktik pengkodean yang tidak aman.
Contoh: Menegakkan Gaya Pengkodean
Katakanlah Anda ingin memastikan bahwa semua nama fungsi dalam proyek Anda mengikuti konvensi snake_case (mis., my_function
bukan myFunction
). Anda dapat menggunakan modul ast
untuk memeriksa pelanggaran.
import ast
import re
class SnakeCaseChecker(ast.NodeVisitor):
def __init__(self):
self.errors = []
def visit_FunctionDef(self, node):
if not re.match(r'^[a-z]+(_[a-z]+)*$', node.name):
self.errors.append(f"Function name '{node.name}' does not follow snake_case convention")
def check_code(self, code):
ast_tree = ast.parse(code)
self.visit(ast_tree)
return self.errors
# Contoh penggunaan
code = """
def myFunction(x):
return x * 2
def calculate_area(width, height):
return width * height
"""
checker = SnakeCaseChecker()
errors = checker.check_code(code)
if errors:
for error in errors:
print(error)
else:
print("Tidak ada pelanggaran gaya yang ditemukan")
Kode ini mendefinisikan kelas SnakeCaseChecker
yang mewarisi dari ast.NodeVisitor
. Metode visit_FunctionDef
memeriksa apakah nama fungsi cocok dengan ekspresi reguler snake_case. Jika tidak, ia menambahkan pesan kesalahan ke daftar errors
. Metode check_code
mengurai kode, melintasi AST, dan mengembalikan daftar kesalahan.
Praktik Terbaik Saat Bekerja dengan Modul ast
- Pahami Struktur AST: Sebelum mencoba memanipulasi AST, luangkan waktu untuk memahami strukturnya menggunakan
ast.dump()
. Ini akan membantu Anda mengidentifikasi node yang perlu Anda kerjakan. - Gunakan
ast.NodeVisitor
danast.NodeTransformer
: Kelas-kelas ini menyediakan cara yang nyaman untuk melintasi dan memodifikasi AST tanpa harus menavigasi pohon secara manual. - Uji Secara Menyeluruh: Saat memodifikasi AST, uji kode Anda secara menyeluruh untuk memastikan bahwa perubahannya benar dan tidak menimbulkan kesalahan apa pun.
- Pertimbangkan
astunparse
untuk Pembuatan Kode: Meskipuncompile()
berguna untuk mengeksekusi kode yang dimodifikasi,astunparse
menyediakan cara untuk menghasilkan kode Python yang dapat dibaca dari AST. - Gunakan Petunjuk Tipe (Type Hints): Petunjuk tipe dapat secara signifikan meningkatkan keterbacaan dan pemeliharaan kode Anda, terutama saat bekerja dengan struktur AST yang kompleks.
- Dokumentasikan Kode Anda: Saat membuat visitor atau transformer AST kustom, dokumentasikan kode Anda dengan jelas untuk menjelaskan tujuan setiap metode dan perubahan yang dibuatnya pada AST.
Tantangan dan Pertimbangan
- Kompleksitas: Bekerja dengan AST bisa jadi rumit, terutama untuk basis kode yang lebih besar. Memahami berbagai jenis node dan hubungannya bisa menjadi tantangan.
- Pemeliharaan: Struktur AST dapat berubah di antara versi Python. Pastikan untuk menguji kode Anda dengan versi Python yang berbeda untuk memastikan kompatibilitas.
- Kinerja: Melintasi dan memodifikasi AST yang besar bisa lambat. Pertimbangkan untuk mengoptimalkan kode Anda untuk meningkatkan kinerja. Caching node yang sering diakses atau menggunakan algoritma yang lebih efisien dapat membantu.
- Penanganan Kesalahan: Tangani kesalahan dengan baik saat mengurai atau memanipulasi AST. Berikan pesan kesalahan yang informatif kepada pengguna.
- Keamanan: Berhati-hatilah saat mengeksekusi kode yang dihasilkan dari AST, terutama jika AST didasarkan pada masukan pengguna. Sanitasi masukan untuk mencegah serangan injeksi kode.
Kesimpulan
Modul ast
Python menyediakan cara yang kuat dan fleksibel untuk berinteraksi dengan abstract syntax tree dari kode Python. Dengan memahami struktur AST dan menggunakan kelas ast.NodeVisitor
dan ast.NodeTransformer
, Anda dapat menganalisis, memodifikasi, dan menghasilkan kode Python secara terprogram. Hal ini membuka pintu ke berbagai aplikasi, mulai dari alat analisis kode hingga refactoring otomatis dan bahkan ekstensi bahasa kustom. Meskipun bekerja dengan AST bisa jadi rumit, manfaat dari kemampuan memanipulasi kode secara terprogram sangatlah signifikan. Manfaatkan kekuatan modul ast
untuk membuka kemungkinan baru dalam proyek Python Anda.